<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="generator" content="AsciiDoc 8.5.2" />
<title></title>
<style type="text/css">
/* Debug borders */
p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 {
/*
  border: 1px solid red;
*/
}

body {
  margin: 1em 5% 1em 5%;
}

a {
  color: blue;
  text-decoration: underline;
}
a:visited {
  color: fuchsia;
}

em {
  font-style: italic;
  color: navy;
}

strong {
  font-weight: bold;
  color: #083194;
}

tt {
  color: navy;
}

h1, h2, h3, h4, h5, h6 {
  color: #527bbd;
  font-family: sans-serif;
  margin-top: 1.2em;
  margin-bottom: 0.5em;
  line-height: 1.3;
}

h1, h2, h3 {
  border-bottom: 2px solid silver;
}
h2 {
  padding-top: 0.5em;
}
h3 {
  float: left;
}
h3 + * {
  clear: left;
}

div.sectionbody {
  font-family: serif;
  margin-left: 0;
}

hr {
  border: 1px solid silver;
}

p {
  margin-top: 0.5em;
  margin-bottom: 0.5em;
}

ul, ol, li > p {
  margin-top: 0;
}

pre {
  padding: 0;
  margin: 0;
}

span#author {
  color: #527bbd;
  font-family: sans-serif;
  font-weight: bold;
  font-size: 1.1em;
}
span#email {
}
span#revnumber, span#revdate, span#revremark {
  font-family: sans-serif;
}

div#footer {
  font-family: sans-serif;
  font-size: small;
  border-top: 2px solid silver;
  padding-top: 0.5em;
  margin-top: 4.0em;
}
div#footer-text {
  float: left;
  padding-bottom: 0.5em;
}
div#footer-badges {
  float: right;
  padding-bottom: 0.5em;
}

div#preamble {
  margin-top: 1.5em;
  margin-bottom: 1.5em;
}
div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
div.admonitionblock {
  margin-top: 1.0em;
  margin-bottom: 1.5em;
}
div.admonitionblock {
  margin-top: 2.0em;
  margin-bottom: 2.0em;
  margin-right: 10%;
  color: #606060;
}

div.content { /* Block element content. */
  padding: 0;
}

/* Block element titles. */
div.title, caption.title {
  color: #527bbd;
  font-family: sans-serif;
  font-weight: bold;
  text-align: left;
  margin-top: 1.0em;
  margin-bottom: 0.5em;
}
div.title + * {
  margin-top: 0;
}

td div.title:first-child {
  margin-top: 0.0em;
}
div.content div.title:first-child {
  margin-top: 0.0em;
}
div.content + div.title {
  margin-top: 0.0em;
}

div.sidebarblock > div.content {
  background: #ffffee;
  border: 1px solid silver;
  padding: 0.5em;
}

div.listingblock > div.content {
  border: 1px solid silver;
  background: #f4f4f4;
  padding: 0.5em;
}

div.quoteblock, div.verseblock {
  padding-left: 1.0em;
  margin-left: 1.0em;
  margin-right: 10%;
  border-left: 5px solid #dddddd;
  color: #777777;
}

div.quoteblock > div.attribution {
  padding-top: 0.5em;
  text-align: right;
}

div.verseblock > div.content {
  white-space: pre;
}
div.verseblock > div.attribution {
  padding-top: 0.75em;
  text-align: left;
}
/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
div.verseblock + div.attribution {
  text-align: left;
}

div.admonitionblock .icon {
  vertical-align: top;
  font-size: 1.1em;
  font-weight: bold;
  text-decoration: underline;
  color: #527bbd;
  padding-right: 0.5em;
}
div.admonitionblock td.content {
  padding-left: 0.5em;
  border-left: 3px solid #dddddd;
}

div.exampleblock > div.content {
  border-left: 3px solid #dddddd;
  padding-left: 0.5em;
}

div.imageblock div.content { padding-left: 0; }
span.image img { border-style: none; }
a.image:visited { color: white; }

dl {
  margin-top: 0.8em;
  margin-bottom: 0.8em;
}
dt {
  margin-top: 0.5em;
  margin-bottom: 0;
  font-style: normal;
  color: navy;
}
dd > *:first-child {
  margin-top: 0.1em;
}

ul, ol {
    list-style-position: outside;
}
ol.arabic {
  list-style-type: decimal;
}
ol.loweralpha {
  list-style-type: lower-alpha;
}
ol.upperalpha {
  list-style-type: upper-alpha;
}
ol.lowerroman {
  list-style-type: lower-roman;
}
ol.upperroman {
  list-style-type: upper-roman;
}

div.compact ul, div.compact ol,
div.compact p, div.compact p,
div.compact div, div.compact div {
  margin-top: 0.1em;
  margin-bottom: 0.1em;
}

div.tableblock > table {
  border: 3px solid #527bbd;
}
thead, p.table.header {
  font-family: sans-serif;
  font-weight: bold;
}
tfoot {
  font-weight: bold;
}
td > div.verse {
  white-space: pre;
}
p.table {
  margin-top: 0;
}
/* Because the table frame attribute is overriden by CSS in most browsers. */
div.tableblock > table[frame="void"] {
  border-style: none;
}
div.tableblock > table[frame="hsides"] {
  border-left-style: none;
  border-right-style: none;
}
div.tableblock > table[frame="vsides"] {
  border-top-style: none;
  border-bottom-style: none;
}


div.hdlist {
  margin-top: 0.8em;
  margin-bottom: 0.8em;
}
div.hdlist tr {
  padding-bottom: 15px;
}
dt.hdlist1.strong, td.hdlist1.strong {
  font-weight: bold;
}
td.hdlist1 {
  vertical-align: top;
  font-style: normal;
  padding-right: 0.8em;
  color: navy;
}
td.hdlist2 {
  vertical-align: top;
}
div.hdlist.compact tr {
  margin: 0;
  padding-bottom: 0;
}

.comment {
  background: yellow;
}

.footnote, .footnoteref {
  font-size: 0.8em;
}

span.footnote, span.footnoteref {
  vertical-align: super;
}

#footnotes {
  margin: 20px 0 20px 0;
  padding: 7px 0 0 0;
}

#footnotes div.footnote {
  margin: 0 0 5px 0;
}

#footnotes hr {
  border: none;
  border-top: 1px solid silver;
  height: 1px;
  text-align: left;
  margin-left: 0;
  width: 20%;
  min-width: 100px;
}


@media print {
  div#footer-badges { display: none; }
}

div#toc {
  margin-bottom: 2.5em;
}

div#toctitle {
  color: #527bbd;
  font-family: sans-serif;
  font-size: 1.1em;
  font-weight: bold;
  margin-top: 1.0em;
  margin-bottom: 0.1em;
}

div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
  margin-top: 0;
  margin-bottom: 0;
}
div.toclevel2 {
  margin-left: 2em;
  font-size: 0.9em;
}
div.toclevel3 {
  margin-left: 4em;
  font-size: 0.9em;
}
div.toclevel4 {
  margin-left: 6em;
  font-size: 0.9em;
}
/* Workarounds for IE6's broken and incomplete CSS2. */

div.sidebar-content {
  background: #ffffee;
  border: 1px solid silver;
  padding: 0.5em;
}
div.sidebar-title, div.image-title {
  color: #527bbd;
  font-family: sans-serif;
  font-weight: bold;
  margin-top: 0.0em;
  margin-bottom: 0.5em;
}

div.listingblock div.content {
  border: 1px solid silver;
  background: #f4f4f4;
  padding: 0.5em;
}

div.quoteblock-attribution {
  padding-top: 0.5em;
  text-align: right;
}

div.verseblock-content {
  white-space: pre;
}
div.verseblock-attribution {
  padding-top: 0.75em;
  text-align: left;
}

div.exampleblock-content {
  border-left: 3px solid #dddddd;
  padding-left: 0.5em;
}

/* IE6 sets dynamically generated links as visited. */
div#toc a:visited { color: blue; }
</style>
<script type="text/javascript">
/*<![CDATA[*/
window.onload = function(){asciidoc.footnotes();}
var asciidoc = {  // Namespace.

/////////////////////////////////////////////////////////////////////
// Table Of Contents generator
/////////////////////////////////////////////////////////////////////

/* Author: Mihai Bazon, September 2002
 * http://students.infoiasi.ro/~mishoo
 *
 * Table Of Content generator
 * Version: 0.4
 *
 * Feel free to use this script under the terms of the GNU General Public
 * License, as long as you do not remove or alter this notice.
 */

 /* modified by Troy D. Hanson, September 2006. License: GPL */
 /* modified by Stuart Rackham, 2006, 2009. License: GPL */

// toclevels = 1..4.
toc: function (toclevels) {

  function getText(el) {
    var text = "";
    for (var i = el.firstChild; i != null; i = i.nextSibling) {
      if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
        text += i.data;
      else if (i.firstChild != null)
        text += getText(i);
    }
    return text;
  }

  function TocEntry(el, text, toclevel) {
    this.element = el;
    this.text = text;
    this.toclevel = toclevel;
  }

  function tocEntries(el, toclevels) {
    var result = new Array;
    var re = new RegExp('[hH]([2-'+(toclevels+1)+'])');
    // Function that scans the DOM tree for header elements (the DOM2
    // nodeIterator API would be a better technique but not supported by all
    // browsers).
    var iterate = function (el) {
      for (var i = el.firstChild; i != null; i = i.nextSibling) {
        if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
          var mo = re.exec(i.tagName);
          if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
            result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
          }
          iterate(i);
        }
      }
    }
    iterate(el);
    return result;
  }

  var toc = document.getElementById("toc");
  var entries = tocEntries(document.getElementById("content"), toclevels);
  for (var i = 0; i < entries.length; ++i) {
    var entry = entries[i];
    if (entry.element.id == "")
      entry.element.id = "_toc_" + i;
    var a = document.createElement("a");
    a.href = "#" + entry.element.id;
    a.appendChild(document.createTextNode(entry.text));
    var div = document.createElement("div");
    div.appendChild(a);
    div.className = "toclevel" + entry.toclevel;
    toc.appendChild(div);
  }
  if (entries.length == 0)
    toc.parentNode.removeChild(toc);
},


/////////////////////////////////////////////////////////////////////
// Footnotes generator
/////////////////////////////////////////////////////////////////////

/* Based on footnote generation code from:
 * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
 */

footnotes: function () {
  var cont = document.getElementById("content");
  var noteholder = document.getElementById("footnotes");
  var spans = cont.getElementsByTagName("span");
  var refs = {};
  var n = 0;
  for (i=0; i<spans.length; i++) {
    if (spans[i].className == "footnote") {
      n++;
      // Use [\s\S] in place of . so multi-line matches work.
      // Because JavaScript has no s (dotall) regex flag.
      note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
      noteholder.innerHTML +=
        "<div class='footnote' id='_footnote_" + n + "'>" +
        "<a href='#_footnoteref_" + n + "' title='Return to text'>" +
        n + "</a>. " + note + "</div>";
      spans[i].innerHTML =
        "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
        "' title='View footnote' class='footnote'>" + n + "</a>]";
      var id =spans[i].getAttribute("id");
      if (id != null) refs["#"+id] = n;
    }
  }
  if (n == 0)
    noteholder.parentNode.removeChild(noteholder);
  else {
    // Process footnoterefs.
    for (i=0; i<spans.length; i++) {
      if (spans[i].className == "footnoteref") {
        var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
        href = href.match(/#.*/)[0];  // Because IE return full URL.
        n = refs[href];
        spans[i].innerHTML =
          "[<a href='#_footnote_" + n +
          "' title='View footnote' class='footnote'>" + n + "</a>]";
      }
    }
  }
}

}
/*]]>*/
</script>
</head>
<body>
<div id="header">
</div>
<div id="content">

<h2 id="_bufferevents_advanced_topics">Bufferevents: advanced topics</h2>
<div class="sectionbody">
<div class="paragraph"><p>This chapter describes some advanced features of Libevent&#8217;s bufferevent
implementation that aren&#8217;t necessary for typical uses.  If you&#8217;re just
learning how to use bufferevents, you should skip this chapter for now
and go on to read <a href="Ref7_evbuffer.html">the evbuffer chapter</a>.</p></div>
<h3 id="_paired_bufferevents">Paired bufferevents</h3><div style="clear:left"></div>
<div class="paragraph"><p>Sometimes you have a networking program that needs to talk to itself.
For example, you could have a program written to tunnel user connections
over some protocol that sometimes also wants to tunnel connections <em>of
its own</em> over that protocol.  You could achieve this by opening a
connection to your own listening port and having your program use
itself, of course, but that would waste resources by having your program
talk to itself via the network stack.</p></div>
<div class="paragraph"><p>Instead, you can create a pair of <em>paired</em> bufferevents such that all bytes
written on one are received on the other (and vice versa), but no actual
platform sockets are used.</p></div>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt><strong>int</strong> bufferevent_pair_new(<strong>struct</strong> event_base *base, <strong>int</strong> options,
    <strong>struct</strong> bufferevent *pair[2]);</tt></pre>
</div></div>
<div class="paragraph"><p>Calling bufferevent_pair_new() sets pair[0] and pair[1] to a pair of
bufferevents, each connected to the other.  All the usual options are
supported, except for BEV_OPT_CLOSE_ON_FREE, which has no effect, and
BEV_OPT_DEFER_CALLBACKS, which is always on.</p></div>
<div class="paragraph"><p>Why do bufferevent pairs need to run with callbacks deferred?  It&#8217;s pretty
common for an operation on one element of the pair to invoke a callback that
alters the bufferevent, thus invoking the other bufferevent&#8217;s callbacks, and
so on through many steps.  When the callbacks were not deferred, this chain
of calls would pretty frequently overflow the stack, starve other
connections, and require all the callbacks to be reentrant.</p></div>
<div class="paragraph"><p>Paired bufferevents support flushing; setting the mode argument to either
either BEV_NORMAL or BEV_FLUSH forces all the relevant data to get
transferred from one bufferevent in the pair to the other, ignoring the
watermarks that would otherwise restrict it.  Setting mode to BEV_FINISHED
additionally generates an EOF event on the opposite bufferevent.</p></div>
<div class="paragraph"><p>Freeing either member of the pair <em>does not</em> automatically free the other or
generate an EOF event; it just makes the other member of the pair become
unlinked.  Once the bufferevent is unlinked, it will no longer successfully
read or write data or generate any events.</p></div>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt><strong>struct</strong> bufferevent *bufferevent_pair_get_partner(<strong>struct</strong> bufferevent *bev)</tt></pre>
</div></div>
<div class="paragraph"><p>Sometimes you may need to get the other member of a bufferevent pair given
only one member.  To do this, you can invoke the
bufferevent_pair_get_partner() function.  It will return the other member of
the pair if <em>bev</em> is a member of a pair, and the other member still exists.
Otherwise, it returns NULL.</p></div>
<div class="paragraph"><p>Bufferevent pairs were new in Libevent 2.0.1-alpha; the
bufferevent_pair_get_partner() function was introduced in Libevent 2.0.6.</p></div>
<h3 id="_filtering_bufferevents">Filtering bufferevents</h3><div style="clear:left"></div>
<div class="paragraph"><p>Sometimes you want to transform all the data passing through a bufferevent
object.  You could do this to add a compression layer, or wrap a protocol in
another protocol for transport.</p></div>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt><strong>enum</strong> bufferevent_filter_result {
        BEV_OK = 0,
        BEV_NEED_MORE = 1,
        BEV_ERROR = 2
};
<strong>typedef</strong> <strong>enum</strong> bufferevent_filter_result (*bufferevent_filter_cb)(
    <strong>struct</strong> evbuffer *source, <strong>struct</strong> evbuffer *destination, ev_ssize_t dst_limit,
    <strong>enum</strong> bufferevent_flush_mode mode, <strong>void</strong> *ctx);


<strong>struct</strong> bufferevent *bufferevent_filter_new(<strong>struct</strong> bufferevent *underlying,
        bufferevent_filter_cb input_filter,
        bufferevent_filter_cb output_filter,
        <strong>int</strong> options,
        <strong>void</strong> (*free_context)(<strong>void</strong> *),
        <strong>void</strong> *ctx);</tt></pre>
</div></div>
<div class="paragraph"><p>The bufferevent_filter_new() function creates a new filtering bufferevent,
wrapped around an existing "underlying" bufferevent.  All data received via
the underlying bufferevent is transformed with the "input" filter before
arriving at the filtering bufferevent, and all data sent via the filtering
bufferevent is transformed with an "output" filter before being sent out to
the underlying bufferevent.</p></div>
<div class="paragraph"><p>Adding a filter to an underlying bufferevent replaces the callbacks on the
underlying bufferevent.  You can still add callbacks to the underlying
bufferevent&#8217;s evbuffers, but you can&#8217;t set the callbacks on the bufferevent
itself if you want the filter to still work.</p></div>
<div class="paragraph"><p>The <em>input_filter</em> and <em>output_filter</em> functions are described below.
All the usual options are supported in <em>options</em>.  If BEV_OPT_CLOSE_ON_FREE
is set, then freeing the filtering bufferevent also frees the underlying
bufferevent.  The <em>ctx</em> field is an arbitrary pointer passed to the filter
functions; if a <em>free_context</em> function is provided, it is called on <em>ctx</em> just
before the filtering bufferevent is closed.</p></div>
<div class="paragraph"><p>The input filter function will be called whenever there is new readable data
on the underlying input buffer.  The output filter function is called
whenever there is new writable data on the filter&#8217;s output buffer.  Each one
receives a pair of evbuffers: a <em>source</em> evbuffer to read data from, and a
<em>destination</em> evbuffer to write data to.  The <em>dst_limit</em> argument describes
the upper bound of bytes to add to <em>destination</em>.  The filter function is
allowed to ignore this value, but doing so might violate high-water marks
or rate limits.  If <em>dst_limit</em> is -1, there is no limit.  The <em>mode</em>
parameter tells the filter how aggressive to be in writing.  If it is
BEV_NORMAL, then it should write as much as can be conveniently transformed.
The BEV_FLUSH value means to write as much as possible, and BEV_FINISHED
means that the filtering function should additionally do any cleanup
necessary at the end of the stream.  Finally, the filter function&#8217;s <em>ctx</em>
argument is a void pointer as provided to the bufferevent_filter_new()
constructor.</p></div>
<div class="paragraph"><p>Filter functions must return BEV_OK if any data was successfully written to
the destination buffer, BEV_NEED_MORE if no more data can be written to the
destination buffer without getting more input or using a different flush
mode, and BEV_ERROR if there is a non-recoverable error on the filter.</p></div>
<div class="paragraph"><p>Creating the filter enables both reading and writing on the underlying
bufferevent.  You do not need to manage reads/writes on your own: the filter
will suspend reading on the underlying bufferevent for you whenever it
doesn&#8217;t want to read.  For 2.0.8-rc and later, it is permissible to
enable/disable reading and writing on the underlying bufferevent
independently from the filter.  If you do this, though, you may keep the
filter from successfully getting the data it wants.</p></div>
<div class="paragraph"><p>You don&#8217;t need to specify both an input filter and an output filter: any
filter you omit is replaced with one that passes data on without transforming
it.</p></div>
<h3 id="_limiting_maximum_single_read_write_size">Limiting maximum single read/write size</h3><div style="clear:left"></div>
<div class="paragraph"><p>By default, bufferevents won&#8217;t read or write the maximum possible amount
of bytes on each invocation of the event loop; doing so can lead to
weird unfair behaviors and resource starvation.  On the other hand, the
defaults might not be reasonable for all situations.</p></div>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt><strong>int</strong> bufferevent_set_max_single_read(<strong>struct</strong> bufferevent *bev, size_t size);
<strong>int</strong> bufferevent_set_max_single_write(<strong>struct</strong> bufferevent *bev, size_t size);

ev_ssize_t bufferevent_get_max_single_read(<strong>struct</strong> bufferevent *bev);
ev_ssize_t bufferevent_get_max_single_write(<strong>struct</strong> bufferevent *bev);</tt></pre>
</div></div>
<div class="paragraph"><p>The two "set" functions replace the current read and write maxima
respectively.  If the <em>size</em> value is 0 or above EV_SSIZE_MAX, they
instead set the maxima to the default value.  These functions return 0
on success and -1 on failure.</p></div>
<div class="paragraph"><p>The two "get" functions return the current per-loop read and write
maxima respectively.</p></div>
<div class="paragraph"><p>These functions were added in 2.1.1-alpha.</p></div>
<h3 id="_bufferevents_and_rate_limiting">Bufferevents and Rate-limiting</h3><div style="clear:left"></div>
<div class="paragraph"><p>Some programs want to limit the amount of bandwidth used for any single
bufferevent, or for a group of bufferevents.  Libevent 2.0.4-alpha and
Libevent 2.0.5-alpha added a basic facility to put caps on individual
bufferevents, or to assign bufferevents to a rate-limited group.</p></div>
<h4 id="_the_rate_limiting_model">The rate-limiting model</h4>
<div class="paragraph"><p>Libevent&#8217;s rate-limiting uses a <em>token bucket</em> algorithm to decide how many
bytes to read or write at a time.  Every rate-limited object, at any given
time, has a "read bucket" and a "write bucket", the sizes of which determine
how many bytes the object is allowed to read or write immediately.  Each
bucket has a refill rate, a maximum burst size, and a
timing unit or "tick".  Whenever the timing unit elapses, the bucket is refilled
proportionally to the refill rate&#8212;but if would become fuller than its burst
size, any excess bytes are lost.</p></div>
<div class="paragraph"><p>Thus, the refill rate determines the maximum average rate at which the object
will send or receive bytes, and the burst size determines the largest number
of bytes that will be sent or received in a single burst.  The timing unit
determines the smoothness of the traffic.</p></div>
<h4 id="_setting_a_rate_limit_on_a_bufferevent">Setting a rate limit on a bufferevent</h4>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt>#define EV_RATE_LIMIT_MAX EV_SSIZE_MAX
<strong>struct</strong> ev_token_bucket_cfg;
<strong>struct</strong> ev_token_bucket_cfg *ev_token_bucket_cfg_new(
        size_t read_rate, size_t read_burst,
        size_t write_rate, size_t write_burst,
        <strong>const</strong> <strong>struct</strong> timeval *tick_len);
<strong>void</strong> ev_token_bucket_cfg_free(<strong>struct</strong> ev_token_bucket_cfg *cfg);
<strong>int</strong> bufferevent_set_rate_limit(<strong>struct</strong> bufferevent *bev,
    <strong>struct</strong> ev_token_bucket_cfg *cfg);</tt></pre>
</div></div>
<div class="paragraph"><p>An <em>ev_token_bucket_cfg</em> structure represents the configuration values for a
pair of token buckets used to limit reading and writing on a single
bufferevent or group of bufferevents.  To create one, call the
ev_token_bucket_cfg_new function and provide the maximum average read rate, the
maximum read burst, the maximum write rate, the maximum write burst, and the
length of a tick.  If the <em>tick_len</em> argument is NULL, the length of a tick
defaults to one second.  The function may return NULL on error.</p></div>
<div class="paragraph"><p>Note that the <em>read_rate</em> and <em>write_rate</em> arguments are scaled in units of
bytes per tick.  That is, if the tick is one tenth of a second, and
<em>read_rate</em> is 300, then the maximum average read rate is 3000 bytes per
second.  Rate and burst values over EV_RATE_LIMIT_MAX are not supported.</p></div>
<div class="paragraph"><p>To limit a bufferevent&#8217;s transfer rate, call bufferevent_set_rate_limit() on
it with an ev_token_bucket_cfg.  The function returns 0 on success, and -1 on
failure.  You can give any number of bufferevents the same
ev_token_bucket_cfg.  To remove a bufferevent&#8217;s rate limits, call
bufferevent_set_rate_limit(), passing NULL for the <em>cfg</em> parameter.</p></div>
<div class="paragraph"><p>To free an ev_token_bucket_cfg, call ev_token_bucket_cfg_free().  Note that
it is NOT currently safe to do this until no bufferevents are using the
ev_token_bucket_cfg.</p></div>
<h4 id="_setting_a_rate_limit_on_a_group_of_bufferevents">Setting a rate limit on a group of bufferevents</h4>
<div class="paragraph"><p>You can assign bufferevents to a <em>rate limiting group</em> if you want to limit
their total bandwidth usage.</p></div>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt><strong>struct</strong> bufferevent_rate_limit_group;

<strong>struct</strong> bufferevent_rate_limit_group *bufferevent_rate_limit_group_new(
        <strong>struct</strong> event_base *base,
        <strong>const</strong> <strong>struct</strong> ev_token_bucket_cfg *cfg);
<strong>int</strong> bufferevent_rate_limit_group_set_cfg(
        <strong>struct</strong> bufferevent_rate_limit_group *group,
        <strong>const</strong> <strong>struct</strong> ev_token_bucket_cfg *cfg);
<strong>void</strong> bufferevent_rate_limit_group_free(<strong>struct</strong> bufferevent_rate_limit_group *);
<strong>int</strong> bufferevent_add_to_rate_limit_group(<strong>struct</strong> bufferevent *bev,
    <strong>struct</strong> bufferevent_rate_limit_group *g);
<strong>int</strong> bufferevent_remove_from_rate_limit_group(<strong>struct</strong> bufferevent *bev);</tt></pre>
</div></div>
<div class="paragraph"><p>To construct a rate limiting group, call bufferevent_rate_limit_group() with an
event_base and an initial ev_token_bucket_cfg.  You can add bufferevents to
the group with bufferevent_add_to_rate_limit_group() and
bufferevent_remove_from_rate_limit_group(); these functions return 0 on
success and -1 on error.</p></div>
<div class="paragraph"><p>A single bufferevent can be a member of no more than one rate limiting group
at a time.  A bufferevent can have both an individual rate limit (as set with
bufferevent_set_rate_limit()) and a group rate limit.  When both limits are
set, the lower limit for each bufferevent applies.</p></div>
<div class="paragraph"><p>You can change the rate limit for an existing group by calling
bufferevent_rate_limit_group_set_cfg().  It returns 0 on success and -1 on
failure.  The bufferevent_rate_limit_group_free() function frees a rate limit
group and removes all of its members.</p></div>
<div class="paragraph"><p>As of version 2.0, Libevent&#8217;s group rate limiting tries to be fair on
aggregate, but the implementation can be unfair on very small timescales.  If
you care strongly about scheduling fairness, please help out
with patches for future versions.</p></div>
<h4 id="_inspecting_current_rate_limit_values">Inspecting current rate-limit values</h4>
<div class="paragraph"><p>Sometimes your code may want to inspect the current rate limits that apply
for a given bufferevent or group.  Libevent provides some functions to do so.</p></div>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt>ev_ssize_t bufferevent_get_read_limit(<strong>struct</strong> bufferevent *bev);
ev_ssize_t bufferevent_get_write_limit(<strong>struct</strong> bufferevent *bev);
ev_ssize_t bufferevent_rate_limit_group_get_read_limit(
        <strong>struct</strong> bufferevent_rate_limit_group *);
ev_ssize_t bufferevent_rate_limit_group_get_write_limit(
        <strong>struct</strong> bufferevent_rate_limit_group *);</tt></pre>
</div></div>
<div class="paragraph"><p>The above functions return the current size, in bytes, of a bufferevent&#8217;s or
a group&#8217;s read or write token buckets.  Note that these values can be
negative if a bufferevent has been forced to exceed its allocations.
(Flushing the bufferevent can do this.)</p></div>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt>ev_ssize_t bufferevent_get_max_to_read(<strong>struct</strong> bufferevent *bev);
ev_ssize_t bufferevent_get_max_to_write(<strong>struct</strong> bufferevent *bev);</tt></pre>
</div></div>
<div class="paragraph"><p>These functions return the number of bytes that a bufferevent would be
willing to read or write right now, taking into account any rate limits that
apply to the bufferevent, its rate limiting group (if any), and any
maximum-to-read/write-at-a-time values imposed by Libevent as a whole.</p></div>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt><strong>void</strong> bufferevent_rate_limit_group_get_totals(
    <strong>struct</strong> bufferevent_rate_limit_group *grp,
    ev_uint64_t *total_read_out, ev_uint64_t *total_written_out);
<strong>void</strong> bufferevent_rate_limit_group_reset_totals(
    <strong>struct</strong> bufferevent_rate_limit_group *grp);</tt></pre>
</div></div>
<div class="paragraph"><p>Each bufferevent_rate_limit_group tracks the total number of bytes sent over
it, in total.  You can use this to track total usage by a number of
bufferevents in the group.   Calling
bufferevent_rate_limit_group_get_totals() on a group sets *<em>total_read_out</em>
and *<em>total_written_out</em> to the total number of bytes read and written on a
bufferevent group respectively.  These totals start at 0 when the group is
created, and reset to 0 whenever bufferevent_rate_limit_group_reset_totals()
is called on a group.</p></div>
<h4 id="_manually_adjusting_rate_limits">Manually adjusting rate limits</h4>
<div class="paragraph"><p>For programs with really complex needs, you might want to adjust the current
values of a token bucket.  You might want to do this, for example, if your
program is generating traffic in some way that isn&#8217;t via a bufferevent.</p></div>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt><strong>int</strong> bufferevent_decrement_read_limit(<strong>struct</strong> bufferevent *bev, ev_ssize_t decr);
<strong>int</strong> bufferevent_decrement_write_limit(<strong>struct</strong> bufferevent *bev, ev_ssize_t decr);
<strong>int</strong> bufferevent_rate_limit_group_decrement_read(
        <strong>struct</strong> bufferevent_rate_limit_group *grp, ev_ssize_t decr);
<strong>int</strong> bufferevent_rate_limit_group_decrement_write(
        <strong>struct</strong> bufferevent_rate_limit_group *grp, ev_ssize_t decr);</tt></pre>
</div></div>
<div class="paragraph"><p>These functions decrement a current read or write bucket in a bufferevent or
rate limiting group.  Note that the decrements are signed: if you want to
increment a bucket, pass a negative value.</p></div>
<h4 id="_setting_the_smallest_share_possible_in_a_rate_limited_group">Setting the smallest share possible in a rate-limited group</h4>
<div class="paragraph"><p>Frequently, you don&#8217;t want to divide the bytes available in a rate-limiting
group up evenly among all bufferevents in every tick.  For example, if you
had 10,000 active bufferevents in a rate-limiting group with 10,000 bytes
available for writing every tick, it wouldn&#8217;t be efficient to let each
bufferevent write only 1 byte per tick, due to the overheads of system calls
and TCP headers.</p></div>
<div class="paragraph"><p>To solve this, each rate-limiting group has a notion of its "minimum share".
In the situation above, instead of every bufferevent being allowed to write 1
byte per tick, 10,000/SHARE bufferevents will be allowed to write SHARE bytes
each every tick, and the rest will be allowed to write nothing.  Which
bufferevents are allowed to write first is chosen randomly each tick.</p></div>
<div class="paragraph"><p>The default minimum share is chosen to give decent performance, and is
currently (as of 2.0.6-rc) set to 64. You can adjust this value with the
following function:</p></div>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt><strong>int</strong> bufferevent_rate_limit_group_set_min_share(
        <strong>struct</strong> bufferevent_rate_limit_group *group, size_t min_share);</tt></pre>
</div></div>
<div class="paragraph"><p>Setting the min_share to 0 disables the minimum-share code entirely.</p></div>
<div class="paragraph"><p>Libevent&#8217;s rate-limiting has had minimum shares since it was first
introduced.  The function to change them was first exposed in Libevent
2.0.6-rc.</p></div>
<h4 id="_limitations_of_the_rate_limiting_implementation">Limitations of the rate-limiting implementation</h4>
<div class="paragraph"><p>As of Libevent 2.0, there are some limitations to the rate-limiting
implementation that you should know.</p></div>
<div class="ulist"><ul>
<li>
<p>
Not every bufferevent type supports rate limiting well, or at all.
</p>
</li>
<li>
<p>
Bufferevent rate limiting groups cannot nest, and a bufferevent can only be
  in a single rate limiting group at a time.
</p>
</li>
<li>
<p>
The rate limiting implementation only counts bytes transferred in TCP
  packets as data, doesn&#8217;t include TCP headers.
</p>
</li>
<li>
<p>
The read-limiting implementation relies on the TCP stack noticing that the
  application is only consuming data at a certain rate, and pushing back on
  the other side of the TCP connection when its buffers get full.
</p>
</li>
<li>
<p>
Some implementations of bufferevents (particularly the windows IOCP
  implementation) can over-commit.
</p>
</li>
<li>
<p>
Buckets start out with one full tick&#8217;s worth of traffic.  This means that a
  bufferevent can start reading or writing immediately, and not wait until a
  full tick has passed.  It also means, though, that a bufferevent that has
  been rate limited for N.1 ticks can potentially transfer N+1 ticks worth of
  traffic.
</p>
</li>
<li>
<p>
Ticks cannot be smaller than 1 millisecond, and all fractions of a
  millisecond are ignored.
</p>
</li>
</ul></div>
<div class="paragraph"><p>/// TODO: Write an example for rate-limiting</p></div>
<h3 id="_bufferevents_and_ssl">Bufferevents and SSL</h3><div style="clear:left"></div>
<div class="paragraph"><p>Bufferevents can use the OpenSSL library to implement the SSL/TLS secure
transport layer.  Because many applications don&#8217;t need or want to link
OpenSSL, this functionality is implemented in a separate library installed as
"libevent_openssl".  Future versions of Libevent could add support for other
SSL/TLS libraries such as NSS or GnuTLS, but right now OpenSSL is all that&#8217;s
there.</p></div>
<div class="paragraph"><p>OpenSSL functionality was introduced in Libevent 2.0.3-alpha, though it didn&#8217;t
work so well before Libevent 2.0.5-beta or Libevent 2.0.6-rc.</p></div>
<div class="paragraph"><p>This section is not a tutorial on OpenSSL, SSL/TLS, or cryptography in general.</p></div>
<div class="paragraph"><p>These functions are all declared in the header "event2/bufferevent_ssl.h".</p></div>
<h4 id="_setting_up_and_using_an_openssl_based_bufferevent">Setting up and using an OpenSSL-based bufferevent</h4>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt><strong>enum</strong> bufferevent_ssl_state {
        BUFFEREVENT_SSL_OPEN = 0,
        BUFFEREVENT_SSL_CONNECTING = 1,
        BUFFEREVENT_SSL_ACCEPTING = 2
};

<strong>struct</strong> bufferevent *
bufferevent_openssl_filter_new(<strong>struct</strong> event_base *base,
    <strong>struct</strong> bufferevent *underlying,
    SSL *ssl,
    <strong>enum</strong> bufferevent_ssl_state state,
    <strong>int</strong> options);

<strong>struct</strong> bufferevent *
bufferevent_openssl_socket_new(<strong>struct</strong> event_base *base,
    evutil_socket_t fd,
    SSL *ssl,
    <strong>enum</strong> bufferevent_ssl_state state,
    <strong>int</strong> options);</tt></pre>
</div></div>
<div class="paragraph"><p>You can create two kinds of SSL bufferevents: a filter-based bufferevent that
communicates over another underlying bufferevent, or a socket-based
bufferevent that tells OpenSSL to communicate with the network directly over.  In
either case, you must provide an SSL object and a description of the SSL
object&#8217;s state.  The state should be BUFFEREVENT_SSL_CONNECTING if the SSL is
currently performing negotiation as a client, BUFFEREVENT_SSL_ACCEPTING
if the SSL is currently performing negotiation as a server, or
BUFFEREVENT_SSL_OPEN if the SSL handshake is done.</p></div>
<div class="paragraph"><p>The usual options are accepted; BEV_OPT_CLOSE_ON_FREE makes the SSL object
and the underlying fd or bufferevent get closed when the openssl bufferevent
itself is closed.</p></div>
<div class="paragraph"><p>Once the handshake is complete, the new bufferevent&#8217;s event callback gets invoked
with BEV_EVENT_CONNECTED in flags.</p></div>
<div class="paragraph"><p>If you&#8217;re creating a socket-based bufferevent and the SSL object already
has a socket set, you do not need to provide the socket yourself: just pass
-1.  You can also set the fd later with bufferevent_setfd().</p></div>
<div class="paragraph"><p>/// TODO: Remove this once bufferevent_shutdown() API has been finished.</p></div>
<div class="paragraph"><p>Note that when BEV_OPT_CLOSE_ON_FREE is set on a SSL bufferevent,
a clean shutdown will not be performed on the SSL connection.  This has two
problems: first, the connection will seem to have been "broken" by the other
side, rather than having been closed cleanly: the other party will not be
able to tell whether you closed the connection, or whether it was broken
by an attacker or third party.  Second, OpenSSL will treat
the session as "bad", and removed from the session cache. This
can cause significant performance degradation on SSL applications under load.</p></div>
<div class="paragraph"><p>Currently the only workaround is to do lazy SSL shutdowns manually. While this
breaks the TLS RFC, it will make sure that sessions will stay in
cache once closed. The following code implements this workaround.</p></div>
<div class="listingblock">
<div class="title">Example</div>
<div class="content">
<pre><tt>SSL *ctx = bufferevent_openssl_get_ssl(bev);

<i>/*</i>
<i> * SSL_RECEIVED_SHUTDOWN tells SSL_shutdown to act as if we had already</i>
<i> * received a close notify from the other end.  SSL_shutdown will then</i>
<i> * send the final close notify in reply.  The other end will receive the</i>
<i> * close notify and send theirs.  By this time, we will have already</i>
<i> * closed the socket and the other end's real close notify will never be</i>
<i> * received.  In effect, both sides will think that they have completed a</i>
<i> * clean shutdown and keep their sessions valid.  This strategy will fail</i>
<i> * if the socket is not ready for writing, in which case this hack will</i>
<i> * lead to an unclean shutdown and lost session on the other end.</i>
<i> */</i>
SSL_set_shutdown(ctx, SSL_RECEIVED_SHUTDOWN);
SSL_shutdown(ctx);
bufferevent_free(bev);</tt></pre>
</div></div>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt>SSL *bufferevent_openssl_get_ssl(<strong>struct</strong> bufferevent *bev);</tt></pre>
</div></div>
<div class="paragraph"><p>This function returns the SSL object used by an OpenSSL bufferevent, or NULL
if <em>bev</em> is not an OpenSSL-based bufferevent.</p></div>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt><strong>unsigned</strong> <strong>long</strong> bufferevent_get_openssl_error(<strong>struct</strong> bufferevent *bev);</tt></pre>
</div></div>
<div class="paragraph"><p>This function returns the first pending OpenSSL error for a given
bufferevent&#8217;s operations, or 0 if there was no pending error.  The error
format is as returned by ERR_get_error() in the openssl library.</p></div>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt><strong>int</strong> bufferevent_ssl_renegotiate(<strong>struct</strong> bufferevent *bev);</tt></pre>
</div></div>
<div class="paragraph"><p>Calling this function tells the SSL to renegotiate, and the bufferevent to
invoke appropriate callbacks.  This is an advanced topic; you should
generally avoid it unless you really know what you&#8217;re doing, especially since
many SSL versions have had known security issues related to
renegotiation.</p></div>
<div class="listingblock">
<div class="title">Interface</div>
<div class="content">
<pre><tt><strong>int</strong> bufferevent_openssl_get_allow_dirty_shutdown(<strong>struct</strong> bufferevent *bev);
<strong>void</strong> bufferevent_openssl_set_allow_dirty_shutdown(<strong>struct</strong> bufferevent *bev,
    <strong>int</strong> allow_dirty_shutdown);</tt></pre>
</div></div>
<div class="paragraph"><p>All good versions of the SSL protocol (that is, SSLv3 and all TLS
versions) support an authenticated shutdown operation that enables the
parties to distinguish an intentional close from an accidental or
maliciously induced termination in the underling buffer.  By default, we
treat anything besides a proper shutdown as an error on the connection.
If the allow_dirty_shutdown flag is set to 1, however, we treat a close
in the connection as a BEV_EVENT_EOF.</p></div>
<div class="paragraph"><p>The allow_dirty_shutdown functions were added in Libevent 2.1.1-alpha.</p></div>
<div class="listingblock">
<div class="title">Example: A simple SSL-based echo server</div>
<div class="content">
<pre><tt><i>/* Simple echo server using OpenSSL bufferevents */</i>
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;arpa/inet.h&gt;

#include &lt;openssl/ssl.h&gt;
#include &lt;openssl/err.h&gt;
#include &lt;openssl/rand.h&gt;

#include &lt;event.h&gt;
#include &lt;event2/listener.h&gt;
#include &lt;event2/bufferevent_ssl.h&gt;

<strong>static</strong> <strong>void</strong>
ssl_readcb(<strong>struct</strong> bufferevent * bev, <strong>void</strong> * arg)
{
    <strong>struct</strong> evbuffer *in = bufferevent_get_input(bev);

    printf("Received %zu bytes\n", evbuffer_get_length(in));
    printf("----- data ----\n");
    printf("%.*s\n", (<strong>int</strong>)evbuffer_get_length(in), evbuffer_pullup(in, -1));

    bufferevent_write_buffer(bev, in);
}

<strong>static</strong> <strong>void</strong>
ssl_acceptcb(<strong>struct</strong> evconnlistener *serv, <strong>int</strong> sock, <strong>struct</strong> sockaddr *sa,
             <strong>int</strong> sa_len, <strong>void</strong> *arg)
{
    <strong>struct</strong> event_base *evbase;
    <strong>struct</strong> bufferevent *bev;
    SSL_CTX *server_ctx;
    SSL *client_ctx;

    server_ctx = (SSL_CTX *)arg;
    client_ctx = SSL_new(server_ctx);
    evbase = evconnlistener_get_base(serv);

    bev = bufferevent_openssl_socket_new(evbase, sock, client_ctx,
                                         BUFFEREVENT_SSL_ACCEPTING,
                                         BEV_OPT_CLOSE_ON_FREE);

    bufferevent_enable(bev, EV_READ);
    bufferevent_setcb(bev, ssl_readcb, NULL, NULL, NULL);
}

<strong>static</strong> SSL_CTX *
evssl_init(<strong>void</strong>)
{
    SSL_CTX  *server_ctx;

<i>    /* Initialize the OpenSSL library */</i>
    SSL_load_error_strings();
    SSL_library_init();
<i>    /* We MUST have entropy, or else there's no point to crypto. */</i>
    <strong>if</strong> (!RAND_poll())
        <strong>return</strong> NULL;

    server_ctx = SSL_CTX_new(SSLv23_server_method());

    <strong>if</strong> (! SSL_CTX_use_certificate_chain_file(server_ctx, "cert") ||
        ! SSL_CTX_use_PrivateKey_file(server_ctx, "pkey", SSL_FILETYPE_PEM)) {
        puts("Couldn't read 'pkey' or 'cert' file.  To generate a key\n"
           "and self-<strong>signed</strong> certificate, run:\n"
           "  openssl genrsa -out pkey 2048\n"
           "  openssl req -<strong>new</strong> -key pkey -out cert.req\n"
           "  openssl x509 -req -days 365 -in cert.req -signkey pkey -out cert");
        <strong>return</strong> NULL;
    }
    SSL_CTX_set_options(server_ctx, SSL_OP_NO_SSLv2);

    <strong>return</strong> server_ctx;
}

<strong>int</strong>
main(<strong>int</strong> argc, <strong>char</strong> **argv)
{
    SSL_CTX *ctx;
    <strong>struct</strong> evconnlistener *listener;
    <strong>struct</strong> event_base *evbase;
    <strong>struct</strong> sockaddr_in sin;

    memset(&amp;sin, 0, <strong>sizeof</strong>(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(9999);
    sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */

    ctx = evssl_init();
    <strong>if</strong> (ctx == NULL)
        <strong>return</strong> 1;
    evbase = event_base_new();
    listener = evconnlistener_new_bind(
                         evbase, ssl_acceptcb, (<strong>void</strong> *)ctx,
                         LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 1024,
                         (<strong>struct</strong> sockaddr *)&amp;sin, <strong>sizeof</strong>(sin));

    event_base_loop(evbase, 0);

    evconnlistener_free(listener);
    SSL_CTX_free(ctx);

    <strong>return</strong> 0;
}</tt></pre>
</div></div>
<h4 id="_some_notes_on_threading_and_openssl">Some notes on threading and OpenSSL</h4>
<div class="paragraph"><p>The built in threading mechanisms of Libevent do not cover OpenSSL locking.
Since OpenSSL uses a myriad of global variables, you must still configure
OpenSSL to be thread safe. While this process is outside the scope of Libevent,
this topic comes up enough to warrant discussion.</p></div>
<div class="listingblock">
<div class="title">Example: A very simple example of how to enable thread safe OpenSSL</div>
<div class="content">
<pre><tt><i>/*</i>
<i> * Please refer to OpenSSL documentation to verify you are doing this correctly,</i>
<i> * Libevent does not guarantee this code is the complete picture, but to be used</i>
<i> * only as an example.</i>
<i> */</i>
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

#include &lt;pthread.h&gt;
#include &lt;openssl/ssl.h&gt;
#include &lt;openssl/crypto.h&gt;

pthread_mutex_t * ssl_locks;
<strong>int</strong> ssl_num_locks;

<i>/* Implements a thread-ID function as requied by openssl */</i>
<strong>static</strong> <strong>unsigned</strong> <strong>long</strong>
get_thread_id_cb(<strong>void</strong>)
{
    <strong>return</strong> (<strong>unsigned</strong> <strong>long</strong>)pthread_self();
}

<strong>static</strong> <strong>void</strong>
thread_lock_cb(<strong>int</strong> mode, <strong>int</strong> which, <strong>const</strong> <strong>char</strong> * f, <strong>int</strong> l)
{
    <strong>if</strong> (which &lt; ssl_num_locks) {
        <strong>if</strong> (mode &amp; CRYPTO_LOCK) {
            pthread_mutex_lock(&amp;(ssl_locks[which]));
        } <strong>else</strong> {
            pthread_mutex_unlock(&amp;(ssl_locks[which]));
        }
    }
}

<strong>int</strong>
init_ssl_locking(<strong>void</strong>)
{
    <strong>int</strong> i;

    ssl_num_locks = CRYPTO_num_locks();
    ssl_locks = malloc(ssl_num_locks * <strong>sizeof</strong>(pthread_mutex_t));
    <strong>if</strong> (ssl_locks == NULL)
        <strong>return</strong> -1;

    <strong>for</strong> (i = 0; i &lt; ssl_num_locks; i++) {
        pthread_mutex_init(&amp;(ssl_locks[i]), NULL);
    }

    CRYPTO_set_id_callback(get_thread_id_cb);
    CRYPTO_set_locking_callback(thread_lock_cb);

    <strong>return</strong> 0;
}</tt></pre>
</div></div>
</div>
</div>
<div id="footnotes"><hr /></div>
<div id="footer">
<div id="footer-text">
Last updated 2013-06-20 11:06:23 EDT
</div>
</div>
</body>
</html>
